---
title: Build Tools & Development Workflow
description: Compare Python and JavaScript build systems, master webpack vs setuptools, learn Babel transpilation, and understand modern JavaScript development workflows
---

# Module 11: Build Tools & Development Workflow

## Learning Objectives

By the end of this module, you will:
- Compare Python and JavaScript build systems
- Understand webpack vs setuptools differences
- Master Babel transpilation and modern JavaScript features
- Set up development servers and hot reload
- Configure production builds and optimization
- Implement CI/CD pipelines for JavaScript projects

## Build Systems Comparison

### Python: setuptools and pip

In Python, you're familiar with:

<PythonEditor title="Python Build System" compare={true}>
```python !! py
# setup.py
from setuptools import setup, find_packages

setup(
    name="my-python-package",
    version="1.0.0",
    description="My awesome Python package",
    author="Your Name",
    author_email="your.email@example.com",
    packages=find_packages(),
    install_requires=[
        "requests>=2.28.0",
        "click>=8.0.0",
        "pydantic>=1.10.0"
    ],
    extras_require={
        "dev": [
            "pytest>=7.0.0",
            "black>=22.0.0",
            "flake8>=5.0.0"
        ]
    },
    entry_points={
        "console_scripts": [
            "my-cli=my_package.cli:main",
        ],
    },
    python_requires=">=3.8",
    classifiers=[
        "Development Status :: 4 - Beta",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3.8+",
    ],
)

# pyproject.toml (modern approach)
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my-python-package"
version = "1.0.0"
description = "My awesome Python package"
dependencies = [
    "requests>=2.28.0",
    "click>=8.0.0"
]

[project.optional-dependencies]
dev = ["pytest>=7.0.0", "black>=22.0.0"]

# Building and distributing
# python -m build
# pip install -e .  # Development install
# twine upload dist/*
```

```javascript !! js
// JavaScript Build System with webpack
// package.json
{
  "name": "my-js-package",
  "version": "1.0.0",
  "description": "My awesome JavaScript package",
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "webpack --mode=production",
    "dev": "webpack serve --mode=development",
    "start": "node dist/index.js",
    "test": "jest",
    "lint": "eslint src/",
    "format": "prettier --write src/"
  },
  "dependencies": {
    "axios": "^1.1.0",
    "lodash": "^4.17.21"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.11.0",
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "babel-loader": "^9.1.0",
    "css-loader": "^6.7.0",
    "style-loader": "^3.3.0",
    "html-webpack-plugin": "^5.5.0"
  },
  "engines": {
    "node": ">=16.0.0"
  },
  "files": [
    "dist/",
    "README.md"
  ]
}

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = (env, argv) => {
  const isProduction = argv.mode === 'production';
  
  return {
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: isProduction ? '[name].[contenthash].js' : '[name].js',
      clean: true,
      library: {
        name: 'MyPackage',
        type: 'umd'
      }
    },
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader'
          }
        },
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
        }
      ]
    },
    plugins: [
      new HtmlWebpackPlugin({
        template: './src/index.html'
      })
    ],
    devServer: {
      contentBase: './dist',
      hot: true,
      port: 3000
    },
    optimization: {
      splitChunks: {
        chunks: 'all'
      }
    }
  };
};

// Building and distributing
// npm run build
// npm publish
```
</PythonEditor>

### Key Build System Differences

| Aspect | Python (setuptools) | JavaScript (webpack) |
|--------|-------------------|---------------------|
| **Configuration** | setup.py/pyproject.toml | webpack.config.js/package.json |
| **Dependencies** | requirements.txt/setup.py | package.json |
| **Build Output** | wheel/egg files | Bundled JS/CSS files |
| **Development** | pip install -e . | webpack-dev-server |
| **Distribution** | PyPI via twine | npm registry |
| **Asset Processing** | Manual/external tools | Built-in loaders |

## Babel Transpilation

### ES6+ to Compatible JavaScript

<PythonEditor title="Babel Transpilation" compare={true}>
```python !! py
# Python doesn't need transpilation
# Version compatibility handled by:
# 1. Language versioning (Python 3.8+)
# 2. Feature detection
# 3. Backporting libraries

# Example: Using modern Python features
from __future__ import annotations  # For older Python versions

def process_data(items: list[dict]) -> dict[str, int]:
    """Process data with type hints (Python 3.9+ syntax)"""
    return {
        item['name']: item['value'] 
        for item in items 
        if item.get('active', False)
    }

# For older Python versions, you might use:
from typing import List, Dict

def process_data_compatible(items: List[Dict]) -> Dict[str, int]:
    return {
        item['name']: item['value'] 
        for item in items 
        if item.get('active', False)
    }

# Virtual environments handle version isolation
# python -m venv venv
# source venv/bin/activate  # or venv\Scripts\activate
```

```javascript !! js
// Modern JavaScript (ES2020+) - Source Code
// src/modern-features.js

// Optional chaining and nullish coalescing
const getUserData = (user) => {
    return {
        name: user?.profile?.name ?? 'Anonymous',
        avatar: user?.profile?.avatar ?? '/default-avatar.png',
        posts: user?.posts?.length ?? 0
    };
};

// Template literals with expressions
const createMessage = (user, action) => `
    User ${user.name} performed ${action} 
    at ${new Date().toISOString()}
`;

// Destructuring with default values
const processApiResponse = ({ data = [], status = 'unknown', ...meta }) => {
    return data.map(item => ({
        ...item,
        processed: true,
        timestamp: Date.now()
    }));
};

// Async/await with proper error handling
const fetchUserData = async (userId) => {
    try {
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
        return await response.json();
    } catch (error) {
        console.error('Failed to fetch user:', error);
        throw error;
    }
};

// Class with private fields (ES2022)
class UserManager {
    #apiKey;
    
    constructor(apiKey) {
        this.#apiKey = apiKey;
    }
    
    async #makeRequest(endpoint) {
        return fetch(endpoint, {
            headers: {
                'Authorization': `Bearer ${this.#apiKey}`
            }
        });
    }
    
    async getUser(id) {
        const response = await this.#makeRequest(`/users/${id}`);
        return response.json();
    }
}

// After Babel transpilation (ES5 compatible)
// dist/compiled.js
"use strict";

function _typeof(obj) {
    "@babel/helpers - typeof";
    return (_typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? 
        function (obj) { return typeof obj; } : 
        function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }
    )(obj);
}

var getUserData = function getUserData(user) {
    var _user$profile, _user$profile2, _user$posts;
    return {
        name: (_user$profile = user === null || user === void 0 ? void 0 : user.profile) === null || _user$profile === void 0 ? void 0 : _user$profile.name,
        avatar: (_user$profile2 = user === null || user === void 0 ? void 0 : user.profile) === null || _user$profile2 === void 0 ? void 0 : _user$profile2.avatar,
        posts: (_user$posts = user === null || user === void 0 ? void 0 : user.posts) === null || _user$posts === void 0 ? void 0 : _user$posts.length
    };
};

// Babel configuration
// .babelrc or babel.config.js
module.exports = {
    presets: [
        ['@babel/preset-env', {
            targets: {
                browsers: ['> 1%', 'last 2 versions', 'not ie <= 8']
            },
            useBuiltIns: 'usage',
            corejs: 3
        }]
    ],
    plugins: [
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-proposal-private-methods',
        '@babel/plugin-proposal-optional-chaining',
        '@babel/plugin-proposal-nullish-coalescing-operator'
    ]
};
```
</PythonEditor>

## Development Servers and Hot Reload

### Development Environment Setup

<PythonEditor title="Development Environment" compare={true}>
```python !! py
# Python development environment
# Usually handled by IDE/editor + file watching

# Using Flask development server
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello, World!"

if __name__ == '__main__':
    # Development server with auto-reload
    app.run(debug=True, host='0.0.0.0', port=5000)

# Using watchdog for file watching
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
import time

class CodeReloadHandler(FileSystemEventHandler):
    def on_modified(self, event):
        if event.src_path.endswith('.py'):
            print(f"File {event.src_path} changed, restarting...")
            # Restart application logic here
            subprocess.run(['python', 'app.py'])

observer = Observer()
observer.schedule(CodeReloadHandler(), path='.', recursive=True)
observer.start()

# Django development server
# python manage.py runserver --settings=myproject.settings.dev

# Using pytest-watch for test-driven development
# pip install pytest-watch
# ptw  # Automatically runs tests when files change
```

```javascript !! js
// JavaScript development with webpack-dev-server
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].bundle.js'
    },
    devServer: {
        contentBase: './dist',
        hot: true,                    // Enable Hot Module Replacement
        open: true,                   // Open browser automatically
        port: 3000,
        historyApiFallback: true,     // For SPA routing
        compress: true,
        overlay: {                    // Show errors in browser
            warnings: true,
            errors: true
        },
        watchOptions: {
            aggregateTimeout: 300,
            poll: 1000,
            ignored: /node_modules/
        },
        proxy: {                      // Proxy API calls
            '/api': {
                target: 'http://localhost:8000',
                changeOrigin: true
            }
        }
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new webpack.HotModuleReplacementPlugin()
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
};

// Hot Module Replacement in application code
// src/index.js
import { updateUI } from './ui.js';
import './styles.css';

function init() {
    updateUI();
    console.log('Application initialized');
}

init();

// Accept hot updates for this module
if (module.hot) {
    module.hot.accept('./ui.js', () => {
        console.log('UI module updated');
        updateUI();
    });
    
    module.hot.accept('./styles.css', () => {
        console.log('Styles updated');
    });
}

// Alternative: Using Vite for faster development
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
    server: {
        port: 3000,
        open: true,
        proxy: {
            '/api': 'http://localhost:8000'
        }
    },
    build: {
        outDir: 'dist',
        sourcemap: true
    },
    css: {
        devSourcemap: true
    }
});

// package.json scripts
{
    "scripts": {
        "dev": "webpack serve --mode=development",
        "dev:vite": "vite",
        "build": "webpack --mode=production",
        "build:vite": "vite build",
        "preview": "vite preview"
    }
}

// Running development server
// npm run dev
// Automatically:
// - Compiles code when files change
// - Refreshes browser
// - Shows errors in overlay
// - Preserves application state (HMR)
```
</PythonEditor>

## Production Build and Optimization

### Build Optimization Strategies

<PythonEditor title="Production Optimization" compare={true}>
```python !! py
# Python production optimization
# Typically handled at deployment level

# Using gunicorn for production WSGI server
# gunicorn --workers 4 --bind 0.0.0.0:8000 app:app

# Optimizing Python code
import functools
import cProfile
import pstats

# Caching with lru_cache
@functools.lru_cache(maxsize=128)
def expensive_computation(n):
    # Some expensive operation
    return sum(i * i for i in range(n))

# Profiling for optimization
def profile_function():
    profiler = cProfile.Profile()
    profiler.enable()
    
    # Your code here
    result = expensive_computation(1000)
    
    profiler.disable()
    stats = pstats.Stats(profiler)
    stats.sort_stats('cumulative')
    stats.print_stats()

# Docker production build
# Dockerfile
FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
RUN python -m compileall .

CMD ["gunicorn", "--workers", "4", "--bind", "0.0.0.0:8000", "app:app"]

# Environment-specific settings
import os

class Config:
    DEBUG = False
    SECRET_KEY = os.environ.get('SECRET_KEY')

class DevelopmentConfig(Config):
    DEBUG = True

class ProductionConfig(Config):
    # Production optimizations
    pass
```

```javascript !! js
// JavaScript production build optimization
// webpack.config.prod.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
    mode: 'production',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[contenthash].js',
        chunkFilename: '[name].[contenthash].chunk.js',
        clean: true,
        publicPath: '/static/'
    },
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    compress: {
                        drop_console: true,    // Remove console.log
                        drop_debugger: true    // Remove debugger statements
                    }
                }
            }),
            new CssMinimizerPlugin()
        ],
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                },
                common: {
                    name: 'common',
                    minChunks: 2,
                    chunks: 'all',
                    enforce: true
                }
            }
        },
        runtimeChunk: 'single'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            ['@babel/preset-env', {
                                targets: '> 0.25%, not dead',
                                useBuiltIns: 'usage',
                                corejs: 3
                            }]
                        ]
                    }
                }
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader'  // For autoprefixer
                ]
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                type: 'asset/resource',
                generator: {
                    filename: 'images/[hash][ext][query]'
                }
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].[contenthash].css'
        }),
        new BundleAnalyzerPlugin({
            analyzerMode: process.env.ANALYZE ? 'server' : 'disabled'
        })
    ],
    performance: {
        hints: 'warning',
        maxEntrypointSize: 250000,
        maxAssetSize: 250000
    }
};

// Advanced optimization techniques
// Tree shaking (automatically enabled in production)
// Only imports used functions
import { debounce } from 'lodash';  // ❌ Imports entire lodash
import debounce from 'lodash/debounce';  // ✅ Only imports debounce

// Code splitting with dynamic imports
const LazyComponent = React.lazy(() => import('./LazyComponent'));

// Route-based code splitting
const routes = [
    {
        path: '/dashboard',
        component: () => import('./Dashboard')
    },
    {
        path: '/profile',
        component: () => import('./Profile')
    }
];

// Service Worker for caching
// sw.js
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
    '/',
    '/static/css/main.css',
    '/static/js/main.js'
];

self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then((cache) => cache.addAll(urlsToCache))
    );
});

// Build scripts in package.json
{
    "scripts": {
        "build": "webpack --config webpack.config.prod.js",
        "build:analyze": "ANALYZE=true npm run build",
        "build:stats": "webpack --config webpack.config.prod.js --json > stats.json",
        "serve:prod": "serve -s dist -l 3000"
    }
}

// Environment variables
// .env.production
NODE_ENV=production
API_URL=https://api.myapp.com
CDN_URL=https://cdn.myapp.com

// webpack.DefinePlugin usage
plugins: [
    new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify('production'),
        'process.env.API_URL': JSON.stringify(process.env.API_URL)
    })
]
```
</PythonEditor>

## Modern Build Tools

### Vite vs Webpack Comparison

<PythonEditor title="Modern Build Tools" compare={true}>
```python !! py
# Python: Poetry as modern dependency management
# pyproject.toml
[tool.poetry]
name = "my-python-app"
version = "0.1.0"
description = "Modern Python application"
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
fastapi = "^0.68.0"
uvicorn = "^0.15.0"
pydantic = "^1.8.0"

[tool.poetry.dev-dependencies]
pytest = "^6.2.0"
black = "^21.9b0"
flake8 = "^3.9.0"
mypy = "^0.910"

[tool.poetry.scripts]
my-app = "my_app.main:app"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

# Commands
# poetry install
# poetry add requests
# poetry add --dev pytest
# poetry build
# poetry publish

# Modern Python tooling
# pyproject.toml configuration for all tools
[tool.black]
line-length = 88
target-version = ['py38']

[tool.mypy]
python_version = "3.8"
warn_return_any = true
strict_optional = true

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
```

```javascript !! js
// Vite: Modern fast build tool
// vite.config.js
import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
    // Fast development with native ES modules
    server: {
        port: 3000,
        hot: true,
        open: true
    },
    
    // Build configuration
    build: {
        outDir: 'dist',
        sourcemap: true,
        rollupOptions: {
            input: {
                main: resolve(__dirname, 'index.html'),
                admin: resolve(__dirname, 'admin.html')
            }
        }
    },
    
    // Plugins
    plugins: [
        // Auto-install missing dependencies
        {
            name: 'auto-install',
            configResolved(config) {
                // Plugin logic
            }
        }
    ],
    
    // CSS preprocessing
    css: {
        preprocessorOptions: {
            scss: {
                additionalData: `@import "@/styles/variables.scss";`
            }
        }
    },
    
    // Environment variables
    define: {
        __APP_VERSION__: JSON.stringify(process.env.npm_package_version)
    },
    
    // Path aliases
    resolve: {
        alias: {
            '@': resolve(__dirname, 'src'),
            '@components': resolve(__dirname, 'src/components')
        }
    }
});

// package.json with modern tools
{
    "name": "modern-js-app",
    "type": "module",  // Use ES modules
    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "preview": "vite preview",
        "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
        "format": "prettier --write .",
        "test": "vitest",
        "test:ui": "vitest --ui"
    },
    "devDependencies": {
        "vite": "^4.0.0",
        "vitest": "^0.25.0",
        "@vitejs/plugin-react": "^3.0.0",
        "eslint": "^8.30.0",
        "prettier": "^2.8.0",
        "typescript": "^4.9.0"
    }
}

// Comparison: Vite vs Webpack
// Vite advantages:
// ✅ Faster cold start (no bundling in dev)
// ✅ Faster HMR (Hot Module Replacement)
// ✅ Native ES modules support
// ✅ Built-in TypeScript support
// ✅ Simpler configuration

// Webpack advantages:
// ✅ More mature ecosystem
// ✅ Better for complex configurations
// ✅ More plugins available
// ✅ Better browser compatibility

// Modern tooling ecosystem
// ESLint configuration
// .eslintrc.js
module.exports = {
    extends: [
        'eslint:recommended',
        '@typescript-eslint/recommended',
        'prettier'
    ],
    plugins: ['@typescript-eslint'],
    rules: {
        'no-console': 'warn',
        'prefer-const': 'error',
        '@typescript-eslint/no-unused-vars': 'error'
    }
};

// Prettier configuration
// .prettierrc
{
    "singleQuote": true,
    "trailingComma": "es5",
    "tabWidth": 2,
    "semi": true,
    "printWidth": 80
}

// TypeScript configuration
// tsconfig.json
{
    "compilerOptions": {
        "target": "ESNext",
        "module": "ESNext",
        "moduleResolution": "node",
        "strict": true,
        "jsx": "preserve",
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "baseUrl": ".",
        "paths": {
            "@/*": ["src/*"],
            "@components/*": ["src/components/*"]
        }
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules", "dist"]
}
```
</PythonEditor>

## CI/CD Pipeline Setup

### Continuous Integration and Deployment

<PythonEditor title="CI/CD Pipelines" compare={true}>
```python !! py
# Python CI/CD with GitHub Actions
# .github/workflows/python-app.yml
name: Python application

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.8, 3.9, "3.10"]

    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v3
      with:
        python-version: ${{ matrix.python-version }}
    
    - name: Cache pip dependencies
      uses: actions/cache@v3
      with:
        path: ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -r requirements-dev.txt
    
    - name: Lint with flake8
      run: |
        flake8 src/ tests/
    
    - name: Format with black
      run: |
        black --check src/ tests/
    
    - name: Type check with mypy
      run: |
        mypy src/
    
    - name: Test with pytest
      run: |
        pytest --cov=src --cov-report=xml
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy to production
      run: |
        # Deployment commands
        echo "Deploying to production"

# Docker deployment
# Dockerfile
FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8000

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]

# docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    depends_on:
      - db
  
  db:
    image: postgres:13
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
```

```javascript !! js
// JavaScript CI/CD with GitHub Actions
// .github/workflows/node.yml
name: Node.js CI

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16.x, 18.x, 20.x]

    steps:
    - uses: actions/checkout@v3
    
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Lint code
      run: npm run lint
    
    - name: Format check
      run: npm run format:check
    
    - name: Type check
      run: npm run type-check
    
    - name: Run tests
      run: npm run test:coverage
    
    - name: Build application
      run: npm run build
    
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-${{ matrix.node-version }}
        path: dist/

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build for production
      run: npm run build
      env:
        NODE_ENV: production
        API_URL: ${{ secrets.API_URL }}
    
    - name: Deploy to AWS S3
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      run: |
        aws s3 sync dist/ s3://my-app-bucket --delete
    
    - name: Invalidate CloudFront
      run: |
        aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_ID }} --paths "/*"

# Multi-stage Docker build
# Dockerfile
FROM node:18-alpine as builder

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

# docker-compose.yml for development
version: '3.8'
services:
  frontend:
    build:
      context: .
      target: development
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
    
  backend:
    image: my-backend:latest
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp

# package.json scripts for CI/CD
{
    "scripts": {
        "dev": "vite",
        "build": "vite build",
        "preview": "vite preview",
        "test": "vitest run",
        "test:watch": "vitest",
        "test:coverage": "vitest run --coverage",
        "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
        "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
        "format": "prettier --write .",
        "format:check": "prettier --check .",
        "type-check": "tsc --noEmit",
        "pre-commit": "lint-staged",
        "prepare": "husky install"
    },
    "lint-staged": {
        "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
        "*.{css,scss,md}": ["prettier --write"]
    }
}

# Netlify deployment configuration
# netlify.toml
[build]
  publish = "dist"
  command = "npm run build"

[build.environment]
  NODE_VERSION = "18"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

# Vercel deployment configuration
# vercel.json
{
    "builds": [
        {
            "src": "package.json",
            "use": "@vercel/static-build",
            "config": {
                "distDir": "dist"
            }
        }
    ],
    "routes": [
        {
            "src": "/(.*)",
            "dest": "/index.html"
        }
    ]
}
```
</PythonEditor>

## Performance Monitoring and Optimization

### Build Performance Analysis

<PythonEditor title="Performance Monitoring" compare={true}>
```python !! py
# Python performance monitoring
import cProfile
import pstats
import time
import functools
from memory_profiler import profile

# Code profiling
def profile_performance():
    profiler = cProfile.Profile()
    profiler.enable()
    
    # Your application code
    expensive_operation()
    
    profiler.disable()
    stats = pstats.Stats(profiler)
    stats.sort_stats('cumulative')
    stats.print_stats(10)  # Top 10 slowest functions

# Memory profiling
@profile
def memory_intensive_function():
    # This will show line-by-line memory usage
    data = [i**2 for i in range(1000000)]
    return sum(data)

# Performance decorators
def timing_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(1)
    return "Done"

# Application monitoring with APM tools
# pip install elastic-apm
from elasticapm.contrib.flask import ElasticAPM

app = Flask(__name__)
app.config['ELASTIC_APM'] = {
    'SERVICE_NAME': 'my-python-app',
    'SECRET_TOKEN': 'your-secret-token',
    'SERVER_URL': 'https://your-apm-server.com'
}
apm = ElasticAPM(app)
```

```javascript !! js
// JavaScript build performance analysis
// webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
    plugins: [
        new BundleAnalyzerPlugin({
            analyzerMode: 'static',
            openAnalyzer: false,
            reportFilename: 'bundle-report.html'
        })
    ]
};

// Speed measurement plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();

module.exports = smp.wrap({
    // Your webpack config
    plugins: [
        // Your plugins
    ]
});

// Bundle size monitoring
// package.json
{
    "scripts": {
        "analyze": "npm run build && npx webpack-bundle-analyzer dist/stats.json",
        "size": "npm run build && size-limit",
        "build:stats": "webpack --json > dist/stats.json"
    },
    "size-limit": [
        {
            "path": "dist/main.*.js",
            "limit": "250 KB"
        },
        {
            "path": "dist/vendor.*.js",
            "limit": "500 KB"
        }
    ]
}

// Performance monitoring in runtime
// Web Vitals monitoring
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

function sendToAnalytics(metric) {
    // Send to your analytics service
    console.log(metric);
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

// Performance API usage
function measurePerformance() {
    // Mark the start of an operation
    performance.mark('operation-start');
    
    // Do some work
    heavyOperation();
    
    // Mark the end
    performance.mark('operation-end');
    
    // Measure the duration
    performance.measure('operation-duration', 'operation-start', 'operation-end');
    
    // Get the measurement
    const measure = performance.getEntriesByName('operation-duration')[0];
    console.log(`Operation took ${measure.duration}ms`);
}

// Lighthouse CI integration
// .lighthouserc.js
module.exports = {
    ci: {
        collect: {
            url: ['http://localhost:3000'],
            startServerCommand: 'npm start',
            numberOfRuns: 3
        },
        assert: {
            assertions: {
                'categories:performance': ['error', { minScore: 0.9 }],
                'categories:accessibility': ['error', { minScore: 0.9 }],
                'categories:best-practices': ['error', { minScore: 0.9 }],
                'categories:seo': ['error', { minScore: 0.9 }]
            }
        },
        upload: {
            target: 'temporary-public-storage'
        }
    }
};

// Build cache optimization
// webpack.config.js
module.exports = {
    cache: {
        type: 'filesystem',
        cacheDirectory: path.resolve(__dirname, '.webpack-cache')
    },
    optimization: {
        moduleIds: 'deterministic',
        runtimeChunk: 'single',
        splitChunks: {
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all'
                }
            }
        }
    }
};

// Source map analysis
// webpack.config.js
module.exports = {
    devtool: process.env.NODE_ENV === 'production' 
        ? 'source-map' 
        : 'eval-source-map',
    optimization: {
        usedExports: true,
        sideEffects: false  // Enable tree shaking
    }
};
```
</PythonEditor>

## Best Practices Summary

### Build Tool Best Practices

1. **Use Modern Tools**: Prefer Vite or modern webpack configs over legacy setups
2. **Optimize for Development**: Fast rebuilds and hot reload for good DX
3. **Production Optimization**: Minification, code splitting, and caching
4. **Monitor Performance**: Use bundle analyzers and performance metrics
5. **Automate Everything**: CI/CD pipelines for testing, building, and deployment

### Development Workflow Best Practices

1. **Consistent Environment**: Use Docker or exact Node.js versions
2. **Code Quality**: ESLint, Prettier, and TypeScript for maintainability
3. **Testing Integration**: Run tests on file changes and in CI/CD
4. **Progressive Enhancement**: Build for modern browsers, fallback for older ones
5. **Security**: Regular dependency updates and vulnerability scanning

## Next Steps

In the next module, we'll put everything together with practical projects that demonstrate real-world JavaScript development scenarios for Python developers.

Key topics we'll cover:
- Full-stack web application
- API integration project
- Frontend component library
- CLI tool development
- Performance optimization case studies

---

*Continue to [Module 12: Practical Projects & Applications →](/docs/py2js/module-12-projects)*
